﻿#pragma once

#include "../../MirEnvir/Envir.h"
#include "../../MessageQueue.h"
#include "NPCPage.h"
#include "NPCChecks.h"
#include "NPCActions.h"
#include "../MapObject.h"
#include "../PlayerObject.h"
#include "../MonsterObject.h"
#include "../../../Shared/Enums.h"
#include <string>
#include <vector>
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <any>
#include <type_traits>
#include "../../../stringhelper.h"
#include "../../../tangible_filesystem.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
namespace S = ServerPackets;

namespace Server::MirObjects
{
    class NPCSegment
    {
    public:
        virtual ~NPCSegment()
        {
            delete Page;
        }

    protected:
        static Server::MirEnvir::Envir *getEnvir();

        static Server::MessageQueue *getMessageQueue();

    public:
        NPCPage *Page;

        const std::string Key;
        std::vector<NPCChecks*> CheckList = std::vector<NPCChecks*>();
        std::vector<NPCActions*> ActList = std::vector<NPCActions*>(), ElseActList = std::vector<NPCActions*>();
        std::vector<std::string> Say, ElseSay, Buttons, ElseButtons, GotoButtons;

        std::string Param1;
        int Param1Instance = 0, Param2 = 0, Param3 = 0;

        std::vector<std::string> Args = std::vector<std::string>();

        NPCSegment(NPCPage *page, std::vector<std::string> &say, std::vector<std::string> &buttons, std::vector<std::string> &elseSay, std::vector<std::string> &elseButtons, std::vector<std::string> &gotoButtons);

        std::vector<std::string> ParseArguments(std::vector<std::string> &words);

        void AddVariable(MapObject *player, const std::string &key, const std::string &value);

        std::string FindVariable(MapObject *player, const std::string &key);

        void ParseCheck(const std::string &line);
        void ParseAct(std::vector<NPCActions*> &acts, const std::string &line);

        std::vector<std::string> ParseSay(PlayerObject *player, std::vector<std::string> &speech);

        std::string ReplaceValue(PlayerObject *player, const std::string &param);
        std::string ReplaceValue(MonsterObject *Monster, const std::string &param);

        bool Check();
        bool Check(MonsterObject *monster);
        bool Check(PlayerObject *player);

    private:
        void Act(std::vector<NPCActions*> &acts);
        void Act(std::vector<NPCActions*> &acts, PlayerObject *player);
        void Act(std::vector<NPCActions*> &acts, MonsterObject *monster);

        void Success(PlayerObject *player);

        void Failed(PlayerObject *player);

        void Success(MonsterObject *Monster);

        void Failed(MonsterObject *Monster);

        void Success();

        void Failed();



    public:
        template<typename T>
        static bool Compare(const std::string &op, T left, T right)
        {
            static_assert(std::is_base_of<IComparable<T>, T>::value, "T must inherit from IComparable<T>");

//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//            switch (op)
//ORIGINAL LINE: case "<":
            if (op == "<")
            {
                    return left.CompareTo(right) < 0;
            }
//ORIGINAL LINE: case ">":
            else if (op == ">")
            {
                    return left.CompareTo(right) > 0;
            }
//ORIGINAL LINE: case "<=":
            else if (op == "<=")
            {
                    return left.CompareTo(right) <= 0;
            }
//ORIGINAL LINE: case ">=":
            else if (op == ">=")
            {
                    return left.CompareTo(right) >= 0;
            }
//ORIGINAL LINE: case "==":
            else if (op == "==")
            {
                    return left.Equals(right);
            }
//ORIGINAL LINE: case "!=":
            else if (op == "!=")
            {
                    return !left.Equals(right);
            }
            else
            {
//C# TO C++ CONVERTER TODO TASK: This exception's constructor requires only one argument:
//ORIGINAL LINE: throw new ArgumentException("Invalid comparison operator: {0}", op);
                    throw std::invalid_argument("Invalid comparison operator: {0}");
            }
        }

        static int Calculate(const std::string &op, int left, int right);
    };
}
